import { beforeEach, describe, expect, it, vi } from "vitest";

import { FormApi } from "../src/form-api";

describe("formApi", () => {
  let formApi: FormApi;

  beforeEach(() => {
    formApi = new FormApi();
  });

  it("should initialize with default state", () => {
    expect(formApi.state).toEqual(
      expect.objectContaining({
        actionWrapperClass: "",
        collapsed: false,
        collapsedRows: 1,
        commonConfig: {},
        handleReset: undefined,
        handleSubmit: undefined,
        layout: "horizontal",
        resetButtonOptions: {},
        schema: [],
        showCollapseButton: false,
        showDefaultActions: true,
        submitButtonOptions: {},
        wrapperClass: "grid-cols-1",
      }),
    );
    expect(formApi.isMounted).toBe(false);
  });

  it("should mount form actions", async () => {
    const formActions: any = {
      meta: {},
      resetForm: vi.fn(),
      setFieldValue: vi.fn(),
      setValues: vi.fn(),
      submitForm: vi.fn(),
      validate: vi.fn(),
      values: { name: "test" },
    };

    await formApi.mount(formActions);
    expect(formApi.isMounted).toBe(true);
    expect(formApi.form).toEqual(formActions);
  });

  it("should get values from form", async () => {
    const formActions: any = {
      meta: {},
      values: { name: "test" },
    };

    await formApi.mount(formActions);
    const values = await formApi.getValues();
    expect(values).toEqual({ name: "test" });
  });

  it("should set field value", async () => {
    const setFieldValueMock = vi.fn();
    const formActions: any = {
      meta: {},
      setFieldValue: setFieldValueMock,
      values: { name: "test" },
    };

    await formApi.mount(formActions);
    await formApi.setFieldValue("name", "new value");
    expect(setFieldValueMock).toHaveBeenCalledWith(
      "name",
      "new value",
      undefined,
    );
  });

  it("should reset form", async () => {
    const resetFormMock = vi.fn();
    const formActions: any = {
      meta: {},
      resetForm: resetFormMock,
      values: { name: "test" },
    };

    await formApi.mount(formActions);
    await formApi.resetForm();
    expect(resetFormMock).toHaveBeenCalled();
  });

  it("should call handleSubmit on submit", async () => {
    const handleSubmitMock = vi.fn();
    const formActions: any = {
      meta: {},
      submitForm: vi.fn().mockResolvedValue(true),
      values: { name: "test" },
    };

    const state = {
      handleSubmit: handleSubmitMock,
    };

    formApi.setState(state);
    await formApi.mount(formActions);

    const result = await formApi.submitForm();
    expect(formActions.submitForm).toHaveBeenCalled();
    expect(handleSubmitMock).toHaveBeenCalledWith({ name: "test" });
    expect(result).toEqual({ name: "test" });
  });

  it("should unmount form and reset state", () => {
    formApi.unmount();
    expect(formApi.isMounted).toBe(false);
  });

  it("should validate form", async () => {
    const validateMock = vi.fn().mockResolvedValue(true);
    const formActions: any = {
      meta: {},
      validate: validateMock,
    };

    await formApi.mount(formActions);
    const isValid = await formApi.validate();
    expect(validateMock).toHaveBeenCalled();
    expect(isValid).toBe(true);
  });
});

describe("updateSchema", () => {
  let instance: FormApi;

  beforeEach(() => {
    instance = new FormApi();
    instance.state = {
      schema: [
        { component: "text", fieldName: "name" },
        { component: "number", fieldName: "age", label: "Age" },
      ],
    };
  });

  it("should update the schema correctly when fieldName matches", () => {
    const newSchema = [
      { component: "text", fieldName: "name" },
      { component: "number", fieldName: "age", label: "Age" },
    ];

    instance.updateSchema(newSchema);

    expect(instance.state?.schema?.[0]?.component).toBe("text");
    expect(instance.state?.schema?.[1]?.label).toBe("Age");
  });

  it("should log an error if fieldName is missing in some items", () => {
    const newSchema: any[] = [
      { component: "textarea", fieldName: "name" },
      { component: "number" },
    ];

    const consoleErrorSpy = vi
      .spyOn(console, "error")
      .mockImplementation(() => {});

    instance.updateSchema(newSchema);

    expect(consoleErrorSpy).toHaveBeenCalledWith(
      "All items in the schema array must have a valid `fieldName` property to be updated",
    );
  });

  it("should not update schema if fieldName does not match", () => {
    const newSchema = [{ component: "textarea", fieldName: "unknown" }];

    instance.updateSchema(newSchema);

    expect(instance.state?.schema?.[0]?.component).toBe("text");
    expect(instance.state?.schema?.[1]?.component).toBe("number");
  });

  it("should not update schema if updatedMap is empty", () => {
    const newSchema: any[] = [{ component: "textarea" }];

    instance.updateSchema(newSchema);

    expect(instance.state?.schema?.[0]?.component).toBe("text");
    expect(instance.state?.schema?.[1]?.component).toBe("number");
  });
});
